#ifndef ODBC_H
#define ODBC_H

class ODBCConnection;
class ODBCQuery;

#include "growbuf.h"

/**
 *  A class that creates connections to the ODBC data source as needed.
 */
class ODBCPool
{
	public:
		ODBCPool();
		~ODBCPool();

		/**
		* Initializes the ODBCPool
		*
		* @return true if the ODBCPool initatied correctly
		*/
		bool8 init();

		/**
		* creates a new connection to the ODBC data source
		*
		* @param dsn the system name of the ODBC data source
		* @param login the login for the dsn
		* @param login the password for the dsn
		* @param timeout the timeout to wait for connections
		*
		* @return a new connection
		*/
		ODBCConnection* open(const String& dsn, const String& login, const String& password,
			int32 timeout = 60);

	protected:
		/**
		* logs error messages 
		*
		* @param reason error reason
		*
		* @return returns the errorcode
		*/
		int32 error(int32 reason);

	protected:
		/**
		* SQL Handle of our enviroment
		*/
		uint32 hEnv;
};

/**
 *  A class that connection to the ODBC data source
 */
class ODBCConnection
{
	friend class ODBCPool;

	public:
		/**
		* Closes the connection
		*/
		~ODBCConnection();

		/**
		* creates a new ODBCQuery
		*/
		ODBCQuery* query();

	protected:
		/**
		* Creates a connections, called by ODBCPool::open
		*/
		ODBCConnection(uint32);

		/**
		* logs error messages 
		*
		* @param reason error reason
		*
		* @return returns the errorcode
		*/
		int32 error(int32 reason);

	protected:
		/**
		* handle to the database connection
		*/
		uint32 hDBc;
};

/**
 *  A class that generates a query for the ODBCConnection
 */
class ODBCQuery
{
	friend class ODBCConnection;

	public:
		~ODBCQuery();

		/**
		* prepare for new stmt after retrieving results
		*/
		bool8 reset();

		/**
		* returns the current query string
		*
		* @return current query string
		*/
		const String& getQuery(); 
		
		/**
		* returns the length of the current query string
		*
		* @return length of the current query string
		*/
		int32 getLength();

		/**
		* sets the query string
		*
		* @param query the new query string
		*/
		void define(const String& query);

		/**
		* appends to the end of the query string
		*
		* @param toEndOfQuery text to append
		*/
		void append(const String& toEndOfQuery);

		/** 
		* Types of Quote Options
		*/
		enum QuoteOption { 
			NOQUOTES,		/**< no quotes  */  
			USEQUOTES,		/**< use quotes:  encloses the string with ' AND
								replaces all ' with '' within the string */ 				 
		};

		/**
		* adds a character parameter to the end of the query string
		*
		* @param ch chatacter to append
		* @param quotes	use quotes?
		*/
		void addParam(const String::Character& ch, QuoteOption quotes = USEQUOTES);

		/**
		* adds a string parameter to the end of the query string
		*
		* @param string string to append
		* @param quotes	use quotes?
		*/
		void addParam(const String& string, QuoteOption quotes = USEQUOTES);

		/**
		* adds a partial string parameter to the end of the query string
		*
		* @param string string to append
		* @param ForcedLen length of string
		* @param quotes	use quotes?
		*/
		void addParam(const String& string, int32 ForcedLen, QuoteOption quotes = USEQUOTES);

		/** 
		* Types of Binary Options
		*/
		enum BinaryOption
		{
			NOTBINARY,	/**< uint8: 0-255, uint8*: raw bytes...dangerous */
			BINARY,		/**< 0x10AE2FC5 */
			HEXSTRING	/**< '10AE2FC5' */
		};

		/**
		* adds a uint8 parameter to the end of the query string
		*
		* @param num number to append
		* @param binary	use binary?
		*/
		void addParam(uint8 num, BinaryOption binary = NOTBINARY);

		/**
		* adds binary data to the end of the query string
		*
		* @param data number to append
		* @param len length of data
		* @param binary	use binary?
		*/
		void addParam(uint8* data, int32 len, BinaryOption binary = HEXSTRING);
		
		/** 
		* Types of Money Options
		*/
		enum MoneyOption { 
			NOTMONEY,		/**< not money  */ 
			MONEY,			/**< money in cents  */ 
			MILLIMONEY		/**< money in millicents  */ 
		};

		/**
		* adds an amount of money to the end of the query string
		*
		* @param amount amount of money
		* @param money type of money
		*/
		void addParam(int32 amount, MoneyOption money = NOTMONEY);

		/**
		* adds an amount of money to the end of the query string
		*
		* @param amount amount of money
		* @param money type of money
		*/
		void addParam(uint32 amount, BinaryOption money = NOTBINARY);

		/**
		* adds an amount of money to the end of the query string
		*
		* @param amount amount of money
		* @param money type of money
		*/
		void addParam(int64 amount, MoneyOption money = NOTMONEY);

		/**
		* executes the stored query
		*
		* @return true if the query succeeds
		*/
		bool8 exec(uint32 queryTimeout);

		/**
		* executes the stored query
		*
		* @return true if the query succeeds
		*/
		bool8 exec();

		/**
		* executes an abitrary query
		*
		* @param query query to execute
		*
		* @return true if the query succeeds
		*/
		bool8 exec(const String& query, uint32 queryTimeout);

		/**
		* executes an abitrary query
		*
		* @param query query to execute
		*
		* @return true if the query succeeds
		*/
		bool8 exec(const String& query);

		/**
		* returns the numbers of columns in the query results
		*
		* @return the numbers of columns in the query results
		*/
		int32 numCols();

		/**
		* gets the next row from the results
		*
		* @return true if there is another row
		*/
		bool8 getRow();

		/**
		* gets character data from the current row
		*
		* @param column	column number to retrieve
		* @param data	data retrieved
		* @param maxLenIncludingNull max data including the null that can be retrieved
		* @param numGotten length of data retrieved
		* @param isNull true if the data is null
		* @param suppressTruncationError ignore truncation errors
		*
		* @return true if the data is retrieved
		*/
		bool8 getData(uint16 column, String* data, bool8* isNull = (bool8*)0, bool8 suppressTruncationError=0);
		
		/**
		* gets character data from the current row
		*
		* @param column	column number to retrieve
		* @param data	data retrieved
		* @param maxLenIncludingNull max data including the null that can be retrieved
		* @param numGotten length of data retrieved
		* @param isNull true if the data is null
		* @param suppressTruncationError ignore truncation errors
		*
		* @return true if the data is retrieved
		*/
		bool8 getData(uint16 column, String& data, bool8* isNull = (bool8*)0, bool8 suppressTruncationError=0);
		
		/**
		* gets character data from the current row
		*
		* @param column	column number to retrieve
		* @param data	data retrieved
		* @param maxLenIncludingNull max data including the null that can be retrieved
		* @param numGotten length of data retrieved
		* @param isNull true if the data is null
		* @param suppressTruncationError ignore truncation errors
		*
		* @return true if the data is retrieved
		*/
		bool8 getData(uint16 column, char* data, int32 maxLenIncludingNull, int32* numGotten = (int32*)0, 
			bool8* isNull = (bool8*)0, bool8 suppressTruncationError=0);
		
		/**
		* gets binary data from the current row
		*
		* @param column	column number to retrieve
		* @param data	data retrieved
		* @param maxLenIncludingNull max data including the null that can be retrieved
		* @param numGotten length of data retrieved
		* @param isNull true if the data is null
		* @param suppressTruncationError ignore truncation errors
		*
		* @return true if the data is retrieved
		*/
		bool8 getData(uint16 column, uint8* data, int32 maxLenIncludingNull,
			int32* numGotten, bool8* isNull = (bool8*)0, bool8 suppressTruncationError=0);
		
		/**
		* gets a money values from the current row
		*
		* @param column	column number to retrieve
		* @param data	data retrieved
		* @param money	what type of money
		* @param isNull true if the data is null
		*
		* @return true if the data is retrieved
		*/
		bool8 getData(uint16 column, int32* data, MoneyOption money = NOTMONEY,
			bool8* isNull = (bool8*)0);

		/**
		* gets a uint32 values from the current row
		*
		* @param column	column number to retrieve
		* @param data	data retrieved
		* @param isNull true if the data is null
		*
		* @return true if the data is retrieved
		*/
		bool8 getData(uint16 column, uint32* data,
			bool8* isNull = (bool8*)0);

		/**
		* gets a int64 values from the current row
		*
		* @param column	column number to retrieve
		* @param data	data retrieved
		* @param isNull true if the data is null
		*
		* @return true if the data is retrieved
		*/
		bool8 getData(uint16 column, int64* data,
			bool8* isNull = (bool8*)0);

		/**
		* gets a int16 values from the current row
		*
		* @param column	column number to retrieve
		* @param data	data retrieved
		* @param isNull true if the data is null
		*
		* @return true if the data is retrieved
		*/
		bool8 getData(uint16 column, int16* data,
			bool8* isNull = (bool8*)0);

		/**
		* gets a uint8 values from the current row
		*
		* @param column	column number to retrieve
		* @param data	data retrieved
		* @param isNull true if the data is null
		*
		* @return true if the data is retrieved
		*/
		bool8 getData(uint16 column, uint8* data,	// 0..255
			bool8* isNull = (bool8*)0);

		/**
		* gets a int8 values from the current row
		*
		* @param column	column number to retrieve
		* @param data	data retrieved
		* @param isNull true if the data is null
		*
		* @return true if the data is retrieved
		*/
		bool8 getData(uint16 column, int8* data,		// -128..127
			bool8* isNull = (bool8*)0);

	protected:
		/**
		* Creates a query, called by ODBCConnection::query
		*/
		ODBCQuery(uint32);

		/**
		* logs error messages 
		*
		* @param reason error reason
		* @param suppressTruncationError suppress truncations errors
		*
		* @return returns the errorcode
		*/
		int32 error(int32 reason, bool8 suppressTruncationError=0);

		/**
		* returns bytes needed for a comma
		*
		* @return bytes needed for a comma
		*/
		int32 commaBytes() { return needComma ? 2 : 0; }

		/**
		* adds a comma to the query if needed
		*/
		void addComma();

		/**
		* gets binary data from the current row
		*
		* this lower level method is typed to the column...the data type MUST match the 
        * colunn (or BOOM) DO NOT make this public...or you'll create hard-to-find bugs!
		* 
		* @param column	column number to retrieve
		* @param data	data retrieved
		* @param datamax max data that can be retrieved
		* @param isNull true if the data is null
		* 
		* @return true if the data is retrieved
		*/
		bool8 getData(uint16 column, void* data, int32 datamax, bool8* isNull);

		/**
		* gets binary data from the current row
		*
		* this lower level method is typed to the column...the data type MUST match the 
        * colunn (or BOOM) DO NOT make this public...or you'll create hard-to-find bugs!
		* 
		* @param column	column number to retrieve
		* @param data	data retrieved
		* @param datamax max data that can be retrieved
		* @param isNull true if the data is null
		* 
		* @return true if the data is retrieved
		*/
        bool8 getData(uint16 column, int64* data, int32 datamax, bool8* isNull);

	protected:
		static uint8 sHex[16];	/**< hex characters */ 

	protected:
		uint32	hStmt;		/**< handle to the query results */ 
		String	query;		/**< current query string */ 
		bool8	needComma;	/**< true if a comma is needed before the next parameter */ 
};


#endif
